
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "dbg_cli.h"



static inline uint32_t  read_reg( uintptr_t addr )
{
   return (*((volatile uint32_t *) (addr)));
}

static inline uint16_t  read_word( uintptr_t addr )
{
   return (*((volatile uint16_t *) (addr)));
}

static inline void  write_reg( uintptr_t addr, uint32_t value )
{
  *(volatile uint32_t *)(addr) = value;
  return;
}


/*
???? 16 ?? ? 10 ??.
*/
int  debug_str2uint( const char * str, uint32_t * pu32 )
{
	char * pend;
	uint64_t  temp;

	/**/
	if ( str[0] == 0x30 )
	{
		if ( str[1] == 0 )
		{
			*pu32 = 0;
			return 0;
		}
		
		if ( (str[1] == 0x58) || (str[1] == 0x78) )
		{
			/**/
			temp = strtoull( str, &pend, 16 );
			if ( pend[0] != 0 )
			{
				return 1;
			}

			/**/
			if ( temp > 0xffffffffull )
			{
				return 2;
			}
		
			/**/
			*pu32 = (uint32_t)temp;
			return 0;
		}
		else
		{
			return 3;
		}
	}

	/**/
	if ( (str[0] >= 0x31) && (str[0] <= 0x39) )
	{
		/**/
		temp = strtoull( str, &pend, 10 );
		if ( pend[0] != 0 )
		{
			return 4;
		}

		/**/
		if ( temp > 0xffffffffull )
		{
			return 5;
		}
		
		/**/
		*pu32 = (uint32_t)temp;		
		return 0;
	}

	/**/
	return 6;
}


void  debug_dump_hex( uint8_t * ptr, size_t len )
{
    int  i;
    int  nn;
    int  len2 = len;

    nn = 0;
    while ( (len2 - nn) >= 16 )
    {
	    printf( "%08x: ", (uintptr_t)(ptr + nn) );
	    
        for ( i=0; i<16; i++ )
        {
            printf( "%02x ", ptr[nn + i] );
        }
        
        printf("  |  ");

        for ( i=0; i<16; i++ )
        {
            int  c = ptr[nn + i];

            if ( (c < 32) || (c > 127) )
                c = '.';
                
            printf("%c", c);
        }

        nn += 16;
        printf("\n");
        
    }

    if ( len2 > nn )
    {
	    printf( "%08x: ", (uintptr_t)(ptr + nn) );
	    
        for ( i = 0; i < (len2-nn); i++ )
        {
            printf("%02x ", ptr[nn + i]);
        }

		/**/
        for ( ; i < 16; i++ )
        {
        	printf( "   " );
       	}
		
        /**/
        printf("  |  ");

        for ( i = 0; i < (len2-nn); i++ )
        {
            int  c = ptr[nn + i];
            
            if (c < 32 || c > 127)
            {
                c = '.';
            }
            
            printf("%c", c);
        }

        printf("\n");
        
    }

	
	/**/
	return;
    
}



int  debug_cmd_dump( void * pctx, int argc, const char **argv )
{
	int  iret;
	uint32_t  addr;
	uint32_t  tcnt;	

	
	/**/
	if ( argc != 3 )
	{
		goto usage;
	}
	
	/**/
	iret = debug_str2uint( argv[1], &addr );
	if ( iret != 0 )
	{
		printf( "addr fmt error..%d.. \n", iret );
		goto usage;
	}
		
	/**/
	iret = debug_str2uint( argv[2], &tcnt );
	if ( iret != 0 ) 
	{
		printf( "cnt fmt error..%d.. \n", iret );
		return 2;
	}

	/**/
	printf( "dump memory, 0x%08lx, %lu\n", addr, tcnt );
	debug_dump_hex( (uint8_t *)(uintptr_t)addr, (int)tcnt );

	
usage:
	printf("usage: %s <addr> <cnt>\n", argv[0] );
	return 0;
		
}



int debug_cmd_wreg( void * pctx, int argc, const char **argv )
{
	int  iret;
	uint32_t  addr;
	uint32_t  value;

	if ( argc != 3 )
	{
		goto usage;
	}
	
	/**/
	iret = debug_str2uint( argv[1], &addr );
	if ( iret != 0 )
	{
		printf( "addr fmt error..%d.. \n", iret );
		goto usage;
	}
		
	/**/
	iret = debug_str2uint( argv[2], &value );
	if ( iret != 0 ) 
	{
		printf( "value fmt error..%d.. \n", iret );
		goto usage;
	}
	
	/**/
	printf( "write reg : %08lx << %08lx\n", addr, value );
	write_reg( (uintptr_t)addr, value );
	return 0;
	
usage:
	printf("usage: %s <addr> <value>\n", argv[0] );	
	return 0;
	
}


int debug_cmd_rreg( void * pctx, int argc, const char **argv )
{
	int  iret;
	uint32_t  temp;
	uintptr_t  addr;
	int  line;
	int  cnt;
	int  i;

	/**/
	if ( argc < 2 )
	{
		goto usage;
	}
	
	/**/
	iret = debug_str2uint( argv[1], &temp );
	if ( iret != 0 )
	{
		printf( "addr fmt error..%d.. \n", iret );
		goto usage;
	}

	addr = temp;
	//addr = addr & 0xFFFFfffc;
	addr = addr & 0xFFFFfffc;
	
	/**/
	cnt = 1;
	if ( argc >= 3 )
	{
	
		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )
		{
			printf( "num fmt error..%d.. \n", iret );
			goto usage;
		}

		cnt = (int)temp;
	}
	
	/**/
	printf( "dump register, 0x%08x, %d\n", addr, cnt );

	line = cnt >> 2;
	for ( i=0; i<line; i++ )
	{
		temp = read_reg( addr );
		printf( "0x%08x:  %08lX", addr, temp );
		addr += 4;

		temp = read_reg( addr );
		printf( "  %08lX", temp );
		addr += 4;

		temp = read_reg( addr );
		printf( "  %08lX", temp );
		addr += 4;

		temp = read_reg( addr );
		printf( "  %08lX\n", temp );
		addr += 4;		
	}

	/**/
	cnt = cnt & 0x3;
	if ( cnt > 0 )
	{
		printf( "0x%08x:", addr );

		for ( i=0; i<cnt; i++ )
		{
			temp = read_reg( addr );
			printf( "  %08lX", temp );
			addr += 4;
		}

		printf( "\n");
	}
	
	
usage:
	printf( "usage: %s <addr> <num>\n", argv[0] );
	return 0;
	
}


int debug_cmd_rword( void * pctx, int argc, const char **argv )
{
	int  iret;
	uint32_t  temp;
	uint16_t  temp2;
	uintptr_t  addr;
	int  line;
	int  cnt;
	int  i;

	/**/
	if ( argc < 2 )
	{
		goto usage;
	}
	
	/**/
	iret = debug_str2uint( argv[1], &temp );
	if ( iret != 0 )
	{
		printf( "addr fmt error..%d.. \n", iret );
		goto usage;
	}

	addr = temp;
	addr = addr & 0xFFFFfffe;
	
	/**/
	cnt = 1;
	if ( argc >= 3 )
	{
	
		iret = debug_str2uint( argv[2], &temp );
		if ( iret != 0 )
		{
			printf( "num fmt error..%d.. \n", iret );
			goto usage;
		}

		cnt = (int)temp;
	}
	
	/**/
	printf( "dump word(16 bits), 0x%08x, %d\n", addr, cnt );

	line = cnt >> 2;
	for ( i=0; i<line; i++ )
	{
		temp2 = read_word( addr );
		printf( "0x%08x:  %04X", addr, temp2 );
		addr += 2;

		temp2 = read_word( addr );
		printf( "  %04X", temp2 );
		addr += 2;

		temp2 = read_word( addr );
		printf( "  %04X", temp2 );
		addr += 2;

		temp2 = read_word( addr );
		printf( "  %04X", temp2 );
		addr += 2;

		temp2 = read_word( addr );
		printf( "  %04X", temp2 );
		addr += 2;

		temp2 = read_word( addr );
		printf( "  %04X", temp2 );
		addr += 2;

		temp2 = read_word( addr );
		printf( "  %04X", temp2 );
		addr += 2;

		temp2 = read_word( addr );
		printf( "  %04X\n", temp2 );
		addr += 2;		
	}

	/**/
	cnt = cnt & 0x7;
	if ( cnt > 0 )
	{
		printf( "0x%08x:", addr );

		for ( i=0; i<cnt; i++ )
		{
			temp2 = read_word( addr );
			printf( "  %04X", temp2 );
			addr += 2;
		}

		printf( "\n");
	}
	
	
usage:
	printf( "usage: %s <addr> <num>\n", argv[0] );
	return 0;
	
}


static cmd_proc_t * dbg_cmds;

/* Command list accessor implementations */
cmd_proc_t * debug_get_cmd_list(void) {
    return dbg_cmds;
}

void debug_set_cmd_list(cmd_proc_t *list) {
    dbg_cmds = list;
}

static int debug_cmd_help( void * parg, int argc, const char **argv )
{
	cmd_proc_t * p;

	/**/
	p = dbg_cmds;

	/**/
	printf( "All Commands:\n\n" );
  
	do {
		printf( "  %s\n", p->cmd );
		p = p->next;
	} while ( p );

	return 0;
}




#if  0
static int debug_cmd_heap( void * parg, int argc, const char **argv )
{
	uint16_t  temp;
	temp = os_get_free_heap_size();
	printf( "heap free size : %u\n", temp );
	return 0;
}
#endif


/**/
int  debug_add_cmd( const char * cmd, debug_func func, void * parg )
{
	cmd_proc_t * p;
	
	/**/
	p = dbg_cmds;

	/* ????. */
	if ( (cmd == NULL) || (func == NULL) )
	{
		return 1;
	}
	
	/* ?????????. */
	if ( 0 == isalpha( (int)(cmd[0]) ) )
	{
		return 2;
	}
		
	/* ????.. */
	while ( p )
	{
		if ( 0 == strcmp( cmd, p->cmd ) )
		{
			return 3;
		}
		
		/**/
		p = p->next;
	}
	
	/* alloc */
	p = (cmd_proc_t *)malloc( sizeof(cmd_proc_t) );

	if ( p == NULL )
	{
		return 4;
	}

	/**/
	p->cmd = cmd;
	p->func = func;
	p->parg = parg;
	p->next = dbg_cmds;
	
	/**/
	dbg_cmds = p;
	return 0;
	
}



static int  debug_process_cmd( int argc, const char ** argv )
{
	cmd_proc_t * p;

	/**/
	p = dbg_cmds;

	if ( 0 == argc )
	{
		return 0;
	}

	/**/
	while (p) 
	{
		/**/
		if ( 0 == strcmp( argv[0], p->cmd ) )
		{
			return p->func( p->parg, argc, argv);
		}

		/**/
		p = p->next;
	}

	printf( "Unknown command: %s\n", argv[0] );
	return 0;
	
}


int  debug_proc_line( char * spad )
{
	/* Check if command list is empty (possible in XIP mode) */
	if (dbg_cmds == NULL) {
		debug_cli_init();
	}

	int  argc;
	const char * argv[10];
	char * p;

	/* split */
	p = spad;
	argc = 0;
	
	while ( 1 )
	{
		if (argc >= 10)
		{
			break;
		}

		/* space, tab */
		while ((*p == 0x20) || (*p == 0x09))
		{
			*p++ = '\0';
		}

		if (*p == '\0')
		{
			break;
		}

		/**/
		argv[argc++] = p;

		/**/
		while ((*p != 0x20) && (*p != 0x09) && (*p != '\0'))
		{
			p++;
		}
	}

	/**/
	debug_process_cmd( argc, argv );
	return 0;
	
}

extern int debug_cmd_echo( void * pctx, int argc, const char **argv );


int  debug_cli_init( void )
{
	/**/
	dbg_cmds = NULL;

	/**/
	debug_add_cmd( "dump", debug_cmd_dump, 		NULL );
	debug_add_cmd( "rword", debug_cmd_rword,     NULL );
	debug_add_cmd( "rreg", debug_cmd_rreg, 		NULL );
	debug_add_cmd( "wreg", debug_cmd_wreg, 		NULL );
	// debug_add_cmd( "heap", debug_cmd_heap, 		NULL );
	debug_add_cmd( "echo", debug_cmd_echo, 		NULL );
	debug_add_cmd( "help", debug_cmd_help, 		NULL );
	
	/**/
	return 0;
}


